﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using DeenGames.Utils;
using Moria.Common.Model;
using Redbrick.Silverlight;
using Redbrick.Silverlight.Common;
using System.Linq;
using Moria.Common.Enum;

namespace Moria.Common
{
	public class TheGame : Observable
	{
		#region Singleton

		//private static readonly Lazy<TheGame> _instance = new Lazy<TheGame>(() => new TheGame());
		//public static TheGame Instance { get { return _instance.Value; } }

		#endregion

		#region Consts & Settings

		private const int CUR_VERSION_MAJ = 5; /* version 5.5 */
		private const int CUR_VERSION_MIN = 5;
		private const int PATCH_LEVEL = 0;
		public const char SaveGameSeperatorToken = '@';
		private const string FileListKey = "savefilelist";

		#endregion Consts & Settings

		#region Declarations

		private readonly CaveFactory _caveFactory;

		private Dictionary<CommandID, ICommand> _commands = new Dictionary<CommandID, ICommand>();
		private int _turn = 0;
		private int _dun_level = -1;
		private DungeonLevel _cave = null;

		private bool _find_flag;
		private bool _player_light;
		private bool _light_flag;
		private bool _teleport_flag = false;


		private Store[] _stores = new Store[] { new Store(StoreType.GeneralStore),new Store(StoreType.Armory),		new Store(StoreType.Weaponsmith),
                                              new Store(StoreType.Temple),		  new  Store(StoreType.AlchemyShop),new Store(StoreType.MagicUserStore)};
		#endregion Declarations

		#region Constructor

		public TheGame()
		{
			this.Version = new Version(CUR_VERSION_MAJ, CUR_VERSION_MIN, PATCH_LEVEL);
			this.Options = GameOptions.Instance;

			this.Player = new Player();
			this.Player.XPositionUpdated += new EventHandler<EventArgs>(Player_XPositionUpdated);
			this.Player.YPositionUpdated += new EventHandler<EventArgs>(Player_YPositionUpdated);

			this._caveFactory = new CaveFactory()
			{
				Turn = () => this.turn,
				IsDebug = false,
			};


			if (!Misc.IsDesignTime(Application.Current.RootVisual))
			{
				this.FileList = PersistentStorage.Retrieve<ObservableCollection<string>>(FileListKey);
				if (this.FileList != null)
					this.FileList = this.FileList.Distinct().ToObservableCollection();
			}
			else
			{
				this.Player.prace = 0;
				this.Player.pclass = 0;
				this.Player.name = "Doom";
			}

			if (this.FileList == null) this.FileList = new ObservableCollection<string>();
			this.FileList.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(FileList_CollectionChanged);

			#region Commands

			this.SetCommand(CommandID.NewGame, Commands.NewGame);

			this.SetCommand(CommandID.SaveGame, Commands.SaveGame);
			((DelegateCommand)this.SaveGameCommand).Executed += new EventHandler<Redbrick.Silverlight.CommandArgs>(this.Save_Game);

			this.SetCommand(CommandID.LoadGame, Commands.RestoreGame);


			this.SetCommand(CommandID.MoveWest, Commands.MoveWest);
			((DelegateCommand)this.MoveWestCommand).Executed += new EventHandler<Redbrick.Silverlight.CommandArgs>(this.Move_West);


			this.SetCommand(CommandID.MoveEast, Commands.MoveEast);
			((DelegateCommand)this.MoveEastCommand).Executed += new EventHandler<Redbrick.Silverlight.CommandArgs>(this.Move_East);

			this.SetCommand(CommandID.MoveNorth, Commands.MoveNorth);
			((DelegateCommand)this.MoveNorthCommand).Executed += new EventHandler<Redbrick.Silverlight.CommandArgs>(this.Move_North);

			this.SetCommand(CommandID.MoveSouth, Commands.MoveSouth);
			((DelegateCommand)this.MoveSouthCommand).Executed += new EventHandler<Redbrick.Silverlight.CommandArgs>(this.Move_South);

			#endregion Commands


			msg_print("Moria " + Version);
		}

		#endregion Constructor

		#region Methods

		public ICommand GetCommand(CommandID cmdKey)
		{
			if (this._commands.ContainsKey(cmdKey))
				return this._commands[cmdKey];
			return null;
		}
		public ICommand SetCommand(CommandID cmdKey, ICommand command)
		{
			return this._commands[cmdKey] = command;

		}

		public void msg_print(string str, params object[] items)
		{
			this.msg_print(string.Format(str, items));
		}
		public void msg_print(string str)
		{
			if (this.Messages == null) this.Messages = new ObservableCollection<string>();
			//this.Messages.Insert(0, str);
			this.Messages.Add(str);
		}

		public bool RestoreTheGame(string file)
		{
			if (this.Player != null)
			{
				this.Player.XPositionUpdated -= this.Player_XPositionUpdated;
				this.Player.YPositionUpdated -= this.Player_YPositionUpdated;
			}
			this.Player = PersistentStorage.Retrieve<Player>(file);

			this.Player.XPositionUpdated += new EventHandler<EventArgs>(Player_XPositionUpdated);
			this.Player.YPositionUpdated += new EventHandler<EventArgs>(Player_YPositionUpdated);

			// remove it so theres no cheating going on!
			if (this.Player != null)
			{
				//TODO: uncomment this
				// this.RemoveSavedGame(file);

				if (this.Player.CurrentDungeonLevel < 0)
					this.Player.CurrentDungeonLevel = 0;
				this.dun_level = this.Player.CurrentDungeonLevel;

				this.msg_print("Welcome back, {0}", this.Player.name);
				this.Cave = _caveFactory.cave;
			}
			return true;
		}

		protected bool SaveTheGame()
		{
			if (this.Player != null && !string.IsNullOrWhiteSpace(this.Player.name))
			{
				string key = PersistentStorage.Store(this.Player.name, this.Player);
				if (!this.FileList.Contains(key))
					this.FileList.Add(this.Player.name + SaveGameSeperatorToken + key);
				this.Player = new Player();
				return true;
			}
			return false;
		}
		protected bool RemoveSavedGame(string file)
		{

			int ndx = this.FileList.FirstIndexOf(str => str.StartsWith(file + SaveGameSeperatorToken));
			if (ndx < 0) return false;
			this.FileList.RemoveAt(ndx);
			PersistentStorage.Remove(file);
			return true;
		}

		protected void OnNewLevel()
		{
			if (this.Cave != null)
			{
				this.Cave.XPositionUpdated -= this.Player_XPositionUpdated;
				this.Cave.YPositionUpdated -= this.Player_YPositionUpdated;
			}

			if (this.dun_level >= 0)
			{
				this.Cave = this._caveFactory.Create(this.dun_level);
				if (this.dun_level == 0)
					this.store_maint();
			}

			if (this.Cave != null)
			{
				this.Player.X = this.Cave.X;
				this.Player.Y = this.Cave.Y;
				this.Cave.XPositionUpdated += new EventHandler<EventArgs>(this.Player_XPositionUpdated);
				this.Cave.YPositionUpdated += new EventHandler<EventArgs>(this.Player_YPositionUpdated);

			}

			if (this.NewLevelGenerated != null)
				this.NewLevelGenerated(this.Cave, new EventArgs());
		}
		protected void OnNewTurn()
		{
			if ((dun_level != 0) && ((turn % 1000) == 0))
				store_maint();
		}

		private void store_maint()
		{
			foreach (Store s_ptr in this._stores)
			{
				s_ptr.InsultCur = 0;
				int j = 0;
				if (s_ptr.Counter >= Store.STORE_MIN_INVEN)
				{
					j = Dice.RandInt(Store.STORE_TURN_AROUND);
					if (s_ptr.Counter >= Store.STORE_MAX_INVEN) j += 1 + s_ptr.Counter - Store.STORE_MAX_INVEN;
					while (--j >= 0)
						s_ptr.store_destroy(Dice.RandInt(s_ptr.Counter) - 1, false);
				}

				if (s_ptr.Counter <= Store.STORE_MAX_INVEN)
				{
					j = Dice.RandInt(Store.STORE_TURN_AROUND);
					if (s_ptr.Counter < Store.STORE_MIN_INVEN)
						j += Store.STORE_MIN_INVEN - s_ptr.Counter;
					while (--j >= 0)
						s_ptr.store_create();
				}
			}
		}

		/// <summary>
		/// was move_char in MORIA3.C
		/// </summary>
		public void Move(Direction dir, bool do_pickup = true)
		{

			if ((this.Player.IsConfused) &&    /* Confused?	     */
				 (Dice.RandInt(4) > 1) &&	    /* 75% random movement   */
				 (dir != Direction.None))		    /* Never random if sitting*/
			{
				dir = (Direction)Dice.RandInt((int)Direction.Max);
				end_find();
			}

			int x = this.Player.X;
			int y = this.Player.Y;

			if (mmove(dir, ref y, ref x))    /* Legal move?	      */
			{
				CaveFloor c_ptr = this.Cave[y, x];

				// if there is no creature, or an unlit creature in the walls then... 
				//disallow attacks against unlit creatures in walls because moving into
				//a wall is a free turn normally, hence don't give player free turns
				//attacking each wall in an attempt to locate the invisible creature,
				//instead force player to tunnel into walls which always takes a turn 
				if (!c_ptr.HasCreature ||
					 (c_ptr.HasCreature && !c_ptr.CreaturePtr.IsLit && c_ptr.FloorType >= FloorType.MinClosedSpace))
				{
					if (c_ptr.FloorType <= FloorType.MaxOpenSpace) /* Open floor spot	*/
					{
						/* Make final assignments of char co-ords */
						int yPrev = this.Player.Y;
						int xPrev = this.Player.X;
						this.Player.Y = y;
						this.Player.X = x;

						/* Check to see if he should stop	       */
						if (_find_flag) area_affect(dir, this.Player.Y, this.Player.X);

						/* Check to see if he notices something  */
						/* fos may be negative if have good rings of searching */
						if ((this.Player.fos <= 1) ||
							 (Dice.RandInt(this.Player.fos) == 1) ||
							 (this.Player.isSearching))
						{
							search(this.Player.Y, this.Player.X, this.Player.srh);
						}

						/* A room of light should be lit.	     */
						if (c_ptr.isLightFloor)
						{
							if (!c_ptr.HasPermLight && !this.Player.IsBlind)
								light_room(this.Player.Y, this.Player.X);
						}
						/* In doorway of light-room?	       */
						else if (c_ptr.IsRoom && (this.Player.blind < 1))
						{
							for (int yCur = (this.Player.Y - 1); yCur <= (this.Player.Y + 1); yCur++)
							{
								for (int xCur = (this.Player.X - 1); xCur <= (this.Player.X + 1); xCur++)
								{
									CaveFloor d_ptr = this.Cave[yCur, xCur];
									if ((d_ptr.isLightFloor) && (!d_ptr.HasPermLight))
										light_room(yCur, xCur);
								}
							}
						}

						/* Move the light source		       */
						move_light(yPrev, xPrev, this.Player.Y, this.Player.X);

						/* An object is beneath him.	     */
						if (c_ptr.HasItem)
						{
							carry(this.Player.Y, this.Player.X, do_pickup);
							/* if stepped on falling rock trap, and space contains	rubble, then step back into a clear area */
							if (c_ptr.ItemPtr.IsRubble)
							{
								move_light(this.Player.Y, this.Player.X, yPrev, xPrev);
								this.Player.Y = yPrev;
								this.Player.X = xPrev;
								/* check to see if we have stepped back onto another trap, if so, set it off */
								c_ptr = this.PlayerFloor;
								if (c_ptr.HasItem)
								{
									if (c_ptr.ItemPtr.IsTrap) // visible or hidden trap or store door
										hit_trap(this.Player.Y, this.Player.X);
								}
							}
						}
					}
					else	  /*Can't move onto floor space*/
					{
						if (!this._find_flag && (c_ptr.HasItem))
						{
							if (c_ptr.ItemPtr.Type == ItemType.RUBBLE) msg_print("There is rubble blocking your way.");
							else if (c_ptr.ItemPtr.Type == ItemType.CLOSED_DOOR) msg_print("There is a closed door blocking your way.");
						}
						else
						{
							end_find();
						}
						FreeTurnFlag = true;
					}
				}
				else	  /* Attacking a creature! */
				{
					bool old_find_flag = _find_flag;
					end_find();
					/* if player can see monster, and was in find mode, then nothing */
					if (c_ptr.CreaturePtr.IsLit && old_find_flag)
					{
						/* did not do anything this turn */
						FreeTurnFlag = true;
					}
					else
					{
						if (this.Player.afraid < 1)		/* Coward?	*/
						{
							this.PlayerAttack(y, x);
						}
						else				/* Coward!	*/
						{
							msg_print("You are too afraid!");
						}
					}
				}
			}
		}

		///<summary>
		/// was player_attack
		/// </summary>
		private void PlayerAttack(int y, int x)
		{
			//TODO: implement PlayerAttack
		}

		private void hit_trap(int y, int x)
		{
			end_find();
			change_trap(y, x);
			CaveFloor c_ptr = this.Cave[y, x];
			InventoryItem t_ptr = c_ptr.ItemPtr;
			int dam = Dice.Roll(t_ptr.Damage);
			string tmp = string.Empty;

			switch (t_ptr.SubCategory)
			{
				case 1:  /* Open pit*/
					{
						msg_print("You fell into a pit!");
						if (this.Player.ffall)
						{
							msg_print("You gently float down.");
						}
						else
						{
							tmp = t_ptr.Description;
							this.take_hit(dam, tmp);
						}
						break;
					}
				case 2: /* Arrow trap*/
					{
						if (Player.test_hit(125, 0, 0, this.Player.pac + Player.ptoac, 4))
						{
							tmp = t_ptr.Description;
							take_hit(dam, tmp);
							msg_print("An arrow hits you.");
						}
						else
							msg_print("An arrow barely misses you.");
						break;
					}
				case 3: /* Covered pit*/
					{
						msg_print("You fell into a covered pit.");
						if (this.Player.ffall)
						{
							msg_print("You gently float down.");
						}
						else
						{
							take_hit(dam, t_ptr.Description);
						}
						this._caveFactory.place_trap(x, y, 0);
						break;
					}
				case 4: /* Trap door*/
					{
						msg_print("You fell through a trap door!");
						// new_level_flag = TRUE;
						dun_level++;
						if (this.Player.ffall)
						{
							msg_print("You gently float down.");
						}
						else
						{
							tmp = t_ptr.Description;
							take_hit(dam, tmp);
						}
						break;
					}
				case 5: /* Sleep gas*/
					{
						if (this.Player.paralysis == 0)
						{
							msg_print("A strange white mist surrounds you!");
							if (this.Player.free_act)
								msg_print("You are unaffected.");
							else
							{
								msg_print("You fall asleep.");
								this.Player.paralysis += Dice.RandInt(10) + 4;
							}
						}
						break;
					}
				case 6: /* Hid Obj*/
					{
						this.delete_object(y, x);
						this.Cave.place_object(y, x);
						msg_print("Hmmm, there was something under this rock.");
						break;
					}
				case 7:  /* STR Dart*/
					{
						if (this.Player.test_hit(125, 0, 0, Player.pac + Player.ptoac, 4))
						{
							if (!this.Player.sustain_str)
							{
								this.Player.dec_stat(Stat.A_STR);
								tmp = t_ptr.Description;
								take_hit(dam, tmp);
								msg_print("A small dart weakens you!");
							}
							else
							{
								msg_print("A small dart hits you.");
							}
						}
						else
						{
							msg_print("A small dart barely misses you.");
						}
						break;
					}
				case 8: /* Teleport*/
					{
						_teleport_flag = true;
						msg_print("You hit a teleport trap!");
						/* Light up the teleport trap, before we teleport away.  */
						move_light(y, x, y, x);
						break;
					}
				case 9: /* Rockfall*/
					{
						take_hit(dam, "a falling rock");
						this.delete_object(y, x);
						this.Cave.place_rubble(y, x);
						msg_print("You are hit by falling rock.");
						break;
					}
				case 10: /* Corrode gas*/
					{
						/* Makes more sense to print the message first, then damage an  object.  */
						msg_print("A strange red gas surrounds you.");
						this.corrode_gas("corrosion gas");
						break;
					}
				case 11: /* Summon mon*/
					{
						this.delete_object(y, x);	/* Rune disappears.    */
						int num = 2 + Dice.RandInt(3);
						for (int i = 0; i < num; i++)
						{
							int ty = y;
							int tx = x;
							this.Cave.summon_monster(ref ty, ref tx, false);
						}
						break;
					}
				case 12: /* Fire trap*/
					{
						msg_print("You are enveloped in flames!");
						this.fire_dam(dam, "a fire trap");
						break;
					}
				case 13: /* Acid trap*/
					{
						msg_print("You are splashed with acid!");
						this.acid_dam(dam, "an acid trap");
						break;
					}
				case 14: /* Poison gas*/
					{
						msg_print("A pungent green gas surrounds you!");
						this.poison_gas(dam, "a poison gas trap");
						break;
					}
				case 15: /* Blind Gas */
					{
						msg_print("A black gas surrounds you!");
						this.Player.blind += Dice.RandInt(50) + 50;
						break;
					}
				case 16: /* Confuse Gas*/
					{
						msg_print("A gas of scintillating colors surrounds you!");
						this.Player.confused += Dice.RandInt(15) + 15;
						break;
					}
				case 17: /* Slow Dart*/
					{
						if (this.Player.test_hit(125, 0, 0, this.Player.pac + this.Player.ptoac, 4))
						{
							tmp = t_ptr.Description;
							take_hit(dam, tmp);
							msg_print("A small dart hits you!");
							if (this.Player.free_act) msg_print("You are unaffected.");
							else this.Player.slow += Dice.RandInt(20) + 10;
						}
						else
						{
							msg_print("A small dart barely misses you.");
						}
						break;
					}
				case 18: /* CON Dart*/
					{
						if (this.Player.test_hit(125, 0, 0, this.Player.pac + this.Player.ptoac, 4))
						{
							if (!this.Player.sustain_con)
							{
								this.Player.dec_stat(Stat.A_CON);
								take_hit(dam, t_ptr.Description);
								msg_print("A small dart saps your health!");
							}
							else
							{
								msg_print("A small dart hits you.");
							}
						}
						else
						{
							msg_print("A small dart barely misses you.");
						} break;
					}
				case 19: /*Secret Door*/
					break;
				case 99: /* Scare Mon*/
					break;

				/* Town level traps are special,	the stores.	*/
				case 101: /* General    */
					enter_store(0);
					break;
				case 102: /* Armory	    */
					enter_store(1);
					break;
				case 103: /* Weaponsmith*/
					enter_store(2);
					break;
				case 104: /* Temple	    */
					enter_store(3);
					break;
				case 105: /* Alchemy    */
					enter_store(4);
					break;
				case 106: /* Magic-User */
					enter_store(5);
					break;

				default:
					msg_print("Unknown trap value.");
					break;
			}
		}

		private void enter_store(int storeID)
		{
			if (this.EnterStore != null)
			{
				this.EnterStore.Invoke(this, new StoreEventArgs(this._stores[storeID]));
			}
		}

		private void poison_gas(int dam, string p)
		{
			//TODO: implement poison_gas
		}

		private void acid_dam(int dam, string p)
		{
			//todo: implement acid_dam
		}

		private void fire_dam(int dam, string p)
		{
			//todo: implement fire_dam
		}

		private void corrode_gas(string msg)
		{
			if (!Player.minus_ac(ItemFlag.ResistAcid)) take_hit(Dice.RandInt(8), msg);
			if (Player.inven_damage(Set.corrodes, 5) > 0) msg_print("There is an acrid smell coming from your pack.");
		}

		private void take_hit(int dam, string tmp)
		{
			//todo: implement take_hit
		}

		private void carry(int y, int x, bool pickup)
		{
			CaveFloor c_ptr = this.Cave[y, x];
			InventoryItem i_ptr = c_ptr.ItemPtr;
			if (i_ptr.Type <= ItemType.MAX_PICK_UP)
			{
				end_find();
				/* There's GOLD in them thar hills!      */
				if (i_ptr.IsGold)
				{
					this.Player.au += i_ptr.Cost;
					msg_print("You have found {0} gold pieces worth of {1}", i_ptr.Cost, i_ptr.GetDescription());
				}
				else
				{
					if (this.Player.inven_check_num(i_ptr))	   /* Too many objects?	    */
					{			    /* Okay,  pick it up      */
						string description = i_ptr.GetDescription();
						if (pickup && this.Options.prompt_carry_flag)
						{
							pickup = this.get_check("Pick up {0}?", description.TrimEnd('.'));
						}
						/* Check to see if it will change the players speed. */
						if (pickup && !this.Player.inven_check_weight(i_ptr))
						{
							pickup = get_check("Exceed your weight limit to pick up {0}?", description.TrimEnd('.'));
							/* Attempt to pick up an object.	       */
							if (pickup)
							{
								int locn = this.Player.inven_carry(i_ptr);
								msg_print("You have {0} ({1})", description, locn + 'a');
								delete_object(y, x);
							}
						}
						else
						{
							msg_print("You can't carry {0}", description);
						}
					}
				}
			}
			else
			{
				if (i_ptr.IsTrap)
				{
					hit_trap(y, x);
				}
			}
		}

		private bool delete_object(int y, int x)
		{
			CaveFloor c_ptr = this.Cave[y, x];
			if (c_ptr.IsBlockedFloor) c_ptr.FloorType = FloorType.Corridor;
			this.Cave.DeleteObject(y, x);
			c_ptr.FieldMark = false;

			if (test_light(y, x)) return true;
			return false;
		}
		private bool test_light(int y, int x)
		{
			return this.Cave[y, x].HasLight;
		}
		private bool get_check(string str, params object[] items)
		{
			//TODO: implement get_check
			return true;
		}

		private void light_room(int p, int p_2)
		{
			//TODO: implement light_room
		}

		private void search(int y, int x, int chance)
		{
			if (this.Player.confused > 0) chance = chance / 10;
			if ((this.Player.blind > 0) || this.no_light) chance = chance / 10;
			if (this.Player.image > 0) chance = chance / 10;
			for (int yCur = (y - 1); yCur <= (y + 1); yCur++)
			{
				for (int xCur = (x - 1); xCur <= (x + 1); xCur++)
				{
					if (Dice.RandInt(100) < chance)	/* always in_bounds here */
					{
						CaveFloor c_ptr = this.Cave[yCur, xCur];
						/* Search for hidden objects		   */
						if (c_ptr.HasItem)
						{
							InventoryItem t_ptr = c_ptr.ItemPtr;
							/* Trap on floor?		       */
							if (t_ptr.IsHiddenTrap)
							{
								msg_print("You have found {0}", t_ptr.GetDescription());
								change_trap(yCur, xCur);
								end_find();
							}
							/* Secret door?		       */
							else if (t_ptr.Type == ItemType.SECRET_DOOR)
							{
								msg_print("You have found a secret door.");
								change_trap(yCur, xCur);
								end_find();
							}
							/* Chest is trapped?	       */
							else if (t_ptr.IsChest)
							{
								/* mask out the treasure bits */
								if (t_ptr.HasTrap)
								{
									if (!t_ptr.IsKnown2)
									{
										t_ptr.IsKnown2 = true;
										msg_print("You have discovered a trap on the chest!");
									}
									else
									{
										msg_print("The chest is trapped!");
									}
								}
							}
						}
					}
				}
			}
		}

		private void change_trap(int y, int x)
		{
			CaveFloor c_ptr = this.Cave[y, x];
			InventoryItem t_ptr = c_ptr.ItemPtr;
			if (t_ptr.IsHiddenTrap)
			{
				t_ptr.Type = ItemType.VIS_TRAP;
			}
			else if (t_ptr.IsSecretDoor)
			{
				/* change secret door to closed door */
				t_ptr.Index = Treasure.OBJ_CLOSED_DOOR;
				t_ptr.Type = Treasure.ClosedDoor.ItemType;
			}
		}


		private void area_affect(Direction dir, int p, int p_2)
		{
			//TODO: implement area_affect
		}

		private bool mmove(Direction dir, ref int y, ref int x)
		{
			int yNew = 0;
			int xNew = 0;

			switch (dir)
			{
				case Direction.SouthWest:
					yNew = y + 1;
					xNew = x - 1;
					break;
				case Direction.South:
					yNew = y + 1;
					xNew = x;
					break;
				case Direction.SouthEast:
					yNew = y + 1;
					xNew = x + 1;
					break;
				case Direction.West:
					yNew = y;
					xNew = x - 1;
					break;
				case Direction.None:
					yNew = y;
					xNew = x;
					break;
				case Direction.East:
					yNew = y;
					xNew = x + 1;
					break;
				case Direction.NorthWest:
					yNew = y - 1;
					xNew = x - 1;
					break;
				case Direction.North:
					yNew = y - 1;
					xNew = x;
					break;
				case Direction.NorthEast:
					yNew = y - 1;
					xNew = x + 1;
					break;
			}

			if ((yNew >= 0) && (yNew < Cave.Height) &&
				 (xNew >= 0) && (xNew < Cave.Width))
			{
				y = yNew;
				x = xNew;
				return true;
			}
			return false;
		}

		/* Switch off the run flag - and get the light correct. -CJS- */
		private void end_find()
		{
			if (_find_flag)
			{
				_find_flag = false;
				move_light(Player.Y, Player.X, Player.Y, Player.X);
			}
		}

		private void move_light(int y1, int x1, int y2, int x2)
		{
			if (this.Player.IsBlind || !_player_light) sub3_move_light(y1, x1, y2, x2);
			else sub1_move_light(y1, x1, y2, x2);
		}

		private void sub1_move_light(int y1, int x1, int y2, int x2)
		{
			if (_light_flag)
			{
				for (int i = y1 - 1; i <= y1 + 1; i++)	   /* Turn off lamp light	*/
				{
					for (int j = x1 - 1; j <= x1 + 1; j++)
					{
						this.Cave[i, j].TempLight = false;
					}
				}
				if (_find_flag && !Options.find_prself)
					_light_flag = false;
			}
			else if (!_find_flag || Options.find_prself)
			{
				_light_flag = true;
			}

			for (int i = y2 - 1; i <= y2 + 1; i++)
			{
				for (int j = x2 - 1; j <= x2 + 1; j++)
				{
					CaveFloor c_ptr = this.Cave[i, j];
					/* only light up if normal movement */
					if (_light_flag) c_ptr.TempLight = true;
					if (c_ptr.FloorType >= FloorType.MinCaveWall) c_ptr.PermLight = true;
					else if (!c_ptr.FieldMark && c_ptr.HasItem)
					{
						if ((c_ptr.ItemPtr.Type >= ItemType.MIN_VISIBLE) && (c_ptr.ItemPtr.Type <= ItemType.MAX_VISIBLE))
							c_ptr.FieldMark = true;
					}
				}
			}
		}

		private void sub3_move_light(int y1, int x1, int y2, int x2)
		{

			if (_light_flag)
			{
				for (int y = y1 - 1; y <= y1 + 1; y++)
				{
					for (int x = x1 - 1; x <= x1 + 1; x++)
					{
						this.Cave[y, x].TempLight = false;
					}
				}
				_light_flag = false;
			}
		}


		#endregion Methods

		#region Events

		/// <summary>
		/// occures when a new level is generated
		/// </summary>
		public event EventHandler<EventArgs> NewLevelGenerated;
		public event EventHandler<StoreEventArgs> EnterStore;
		public event EventHandler<EventArgs> GameSaved;

		protected override bool OnPropertyChanged(PropertyChangedEventArgs args)
		{
			base.OnPropertyChanged(args);
			switch (args.PropertyName)
			{
				case "turn":
					this.OnNewTurn();
					break;

				case "dun_level":
					this.OnNewLevel();
					break;
			}
			return true;
		}

		protected void Player_XPositionUpdated(object sender, EventArgs e)
		{
			if (sender is Player)
			{
				this.Cave.X = this.Player.X;
			}
			else if (sender is DungeonLevel)
			{
				this.Player.X = this.Cave.X;
			}

		}
		protected void Player_YPositionUpdated(object sender, EventArgs e)
		{
			if (sender is Player)
			{
				this.Cave.Y = this.Player.Y;
			}
			else if (sender is DungeonLevel)
			{
				this.Player.Y = this.Cave.Y;
			}

		}

		private void FileList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
		{
			PersistentStorage.Store(FileListKey, this.FileList);
		}

		#region Command Handlers

		void Move_West(object sender, CommandArgs e)
		{
			this.Move(Direction.West);
			e.Handled = true;

		}
		void Move_East(object sender, CommandArgs e)
		{
			this.Move(Direction.East);
			e.Handled = true;

		}
		void Move_North(object sender, CommandArgs e)
		{
			this.Move(Direction.North);
			e.Handled = true;

		}
		void Move_South(object sender, CommandArgs e)
		{
			this.Move(Direction.South);
			e.Handled = true;

		}

		void Save_Game(object sender, CommandArgs e)
		{
			if (this.SaveTheGame())
			{
				if (this.GameSaved != null)
					this.GameSaved.Invoke(this, new EventArgs());
				e.Handled = true;
			}
		}
		void Restore_Game(object sender, CommandArgs e)
		{
			this.RestoreTheGame("");
		}

		#endregion Command Handlers

		#endregion Events

		#region Properties

		#region Commands

		public ICommand NewGameCommand { get { return this.GetCommand(CommandID.NewGame); } }
		public ICommand MoveNorthCommand { get { return this.GetCommand(CommandID.MoveNorth); } }
		public ICommand MoveSouthCommand { get { return this.GetCommand(CommandID.MoveSouth); } }
		public ICommand MoveWestCommand { get { return this.GetCommand(CommandID.MoveWest); } }
		public ICommand MoveEastCommand { get { return this.GetCommand(CommandID.MoveEast); } }


		public DelegateCommand SaveGameCommand { get { return Commands.SaveGame; } }
		public DelegateCommand RestoreGameCommand { get { return Commands.RestoreGame; } }
		public DelegateCommand ViewScoresCommand { get { return Commands.ViewScores; } }

		#endregion

		private bool no_light
		{
			get
			{
				CaveFloor c_ptr = this.PlayerFloor;
				return (!c_ptr.HasTempLight && !c_ptr.HasPermLight);
			}
		}

		public GameOptions Options { get; private set; }
		public ObservableCollection<string> FileList { get; private set; }
		public ObservableCollection<string> Messages { get; private set; }

		public Version Version { get; private set; }
		public Player Player { get; private set; }
		public int dun_level
		{
			get { return this._dun_level; }
			set
			{
				if (value == this._dun_level) return;
				this._dun_level = value;
				this.RaisePropertyChanged(() => this.dun_level);
			}
		}
		public int turn
		{
			get { return this._turn; }
			set
			{
				if (value == this._turn) return;
				this._turn = value;
				this.RaisePropertyChanged(() => this.turn);
			}
		}
		public DungeonLevel Cave
		{
			get { return this._cave; }
			private set
			{
				if (value == this._cave) return;
				this._cave = value;
				this.RaisePropertyChanged(() => this.Cave);
			}
		}
		/// <summary>
		/// get the cave floor where the player is at
		/// </summary>
		private CaveFloor PlayerFloor { get { return this.Cave[this.Player.Point]; } }
		/// <summary>
		/// was free_turn_flag
		/// </summary>
		public bool FreeTurnFlag { get; set; }

		#endregion Properties
	}
}

